// Copyright (c) 1996-2002 John Lyon-Smith. All rights reserved.

#pragma once

#include <wxtl\Base.h>
#include <wxtl\File.h>

namespace wxtl
{
	inline tstring _stdcall addDirSep(const TCHAR* str)
	{
		if (str[_tcslen(str) - 1] != '\\')
			return tstring(str) + _T("\\");
		else
			return str;
	}

	inline tstring _stdcall removeDirSep(const TCHAR* str)
	{
		size_t n = _tcslen(str) - 1;
		
		if (str[n] == '\\')
			return tstring(str, 0, n);
		else
			return str;
	}

	inline tstring _stdcall getCurrentDir()
	{
		TCHAR szBuf[WXTL_BIG_MAX_PATH];

		::GetCurrentDirectory(WXTL_BIG_MAX_PATH, szBuf);

		tstring str(szBuf);

		str = addDirSep(str.c_str());
		
		return str;
	}

	inline bool _stdcall setCurrentDir(const TCHAR *pszPath)
	{
		return ::SetCurrentDirectory(pszPath) == TRUE;
	}

	enum PATH_PARTS
	{
		pathDrive,
		pathDir,
		pathFile,
		pathExt,
		pathDriveDir,
		pathDirFile,
		pathFileExt,
		pathDriveDirFile,
		pathDirFileExt,
		pathAll,
	};

	inline tstring _stdcall getPathParts(const TCHAR* pszPath, PATH_PARTS parts)
	{
		TCHAR szDrive[_MAX_DRIVE];
		TCHAR szDir[_MAX_DIR];
		TCHAR szFile[_MAX_FNAME];
		TCHAR szExt[_MAX_EXT];
		tstring str;

		_tsplitpath_s(pszPath, szDrive,_MAX_DRIVE, szDir,_MAX_DIR, szFile,_MAX_FNAME, szExt,_MAX_EXT);

		switch (parts)
		{
		case pathDrive:
			str = szDrive;
			break;
		case pathDir:
			str = szDir;
			break;
		case pathFile:
			str = szFile;
			break;
		case pathExt:
			str = szExt;
			break;
		case pathDriveDir:
			str = szDrive;
			str += szDir;
			break;
		case pathDirFile:
			str = szDir;
			str += szFile;
			break;
		case pathFileExt:
			str = szFile;
			str += szExt;	
			break;
		case pathDriveDirFile:
			str = szDrive;
			str += szDir;
			str += szFile;
			break;
		case pathDirFileExt:
			str = szDir;
			str += szFile;
			str += szExt;
			break;
		case pathAll:
			str = pszPath;
			break;
		}

		return str;
	}

	inline tstring _stdcall expandPath(const TCHAR* pszRelPath, const TCHAR* pszAbsPath = NULL) throw(invalid_argument)
	{
		if (*pszRelPath == NULL || pszRelPath[0] == 0)
			throw invalid_argument("path is empty");

		// BUGBUG: If the path we are given starts with a drive letter, we assume we're done
		if (pszRelPath[1] == ':')
			return tstring(pszRelPath);
		
		// If it's UNC, we're done
		if (pszRelPath[0] == '\\' && pszRelPath[1] == '\\')
			throw invalid_argument("unc paths not supported");
		
		tstring strAbsPath;

		if (pszAbsPath == NULL)
			strAbsPath = getCurrentDir();
		else
		{
			strAbsPath = pszAbsPath;

			if (strAbsPath.length() < 3)
				throw invalid_argument("absolute path is too short");
		}

		strAbsPath = addDirSep(strAbsPath.c_str());

		// Do we just need to add a drive letter?
		if (pszRelPath[0] == '\\')
			return getPathParts(strAbsPath.c_str(), pathDrive) + pszRelPath;
		
		tstring strFile(getPathParts(pszRelPath, pathFileExt));
		tstring strT(getPathParts(pszRelPath, pathDriveDir));

		if (!strT.empty())
		{
			size_t nLast = strT.size() - 1;
			size_t i = 0;
			for (; strT[i] == '.' && i < nLast;)
			{
				if (strT[i+1] == '.')
				{
					// Chop off last directory
					size_t n = strAbsPath.substr(0, strAbsPath.size() - 1).rfind('\\');

					if (n == string::npos)
						throw invalid_argument("badly formed relative path");

					strAbsPath = strAbsPath.substr(0, n+1);

					if (strT[i+2] == '\\')
						i += 3;
					else
						i += 2;
				}
				else
				{
					// Only accept one '.' at beginning
					if (strT[i+1] == '\\')
						i += 2;
					else
						i++;

					break;
				}
			}

			strT = strT.substr(i);
		}
		
		return strAbsPath + strT + strFile;
	}

	// Useful Windows directories
	inline tstring _stdcall getModulePath(HMODULE hMod = NULL)
	{
		TCHAR szBuf[WXTL_BIG_MAX_PATH];
		
		GetModuleFileName(hMod, szBuf, WXTL_BIG_MAX_PATH);

		return tstring(szBuf);
	}

	inline tstring _stdcall getSystemPath(void)
	{
		TCHAR szBuf[WXTL_BIG_MAX_PATH];

		GetSystemDirectory(szBuf, WXTL_BIG_MAX_PATH);

		return tstring(szBuf);
	}

	inline tstring _stdcall getModuleName(HMODULE hMod = NULL)
	{
		return getPathParts(getModulePath(hMod).c_str(), pathFile);
	}

	inline tstring _stdcall getModuleDir(HMODULE hMod = NULL)
	{
		return getPathParts(getModulePath(hMod).c_str(), pathDriveDir);
	}

	// Finding files and directories
	inline void _stdcall findPaths(
		vector<tstring> &paths, 
		const TCHAR* pathNameMask, 
		DWORD dwAttrs = 
		File::attrNormal | File::attrArchive)
	{
		WIN32_FIND_DATA fd = {0};

		paths.clear();

		HANDLE hFF = FindFirstFile(pathNameMask, &fd);

		if (hFF == INVALID_HANDLE_VALUE)
			return;

		tstring s;

		if (fd.dwFileAttributes & dwAttrs)
		{
			s = fd.cFileName;

			if (dwAttrs && s != _T(".") && s != _T(".."))
				paths.push_back(s);
		}

		while (FindNextFile(hFF, &fd))
		{
			if (fd.dwFileAttributes & dwAttrs)
			{
				s = fd.cFileName;

				if (dwAttrs && s != _T(".") && s != _T(".."))
					paths.push_back(s);
			}
		}

		FindClose(hFF);
	}

	inline bool _stdcall fileExists(const TCHAR *pszPath)
	{
		DWORD dwAttr = GetFileAttributes(pszPath);
		
		return (dwAttr != (DWORD)-1) && !(dwAttr & FILE_ATTRIBUTE_DIRECTORY);
	}

	inline bool _stdcall dirExists(const TCHAR *pszPath)
	{
		DWORD dwAttr = GetFileAttributes(pszPath);
		
		return (dwAttr != (DWORD)-1) && (dwAttr & FILE_ATTRIBUTE_DIRECTORY);
	}
}

